package elements

import (
	"github.com/pkg/errors"
	"yizhuoyan/go-study/pdfgo/raw/contents"
	"yizhuoyan/go-study/pdfgo/raw/objects/basicobject"
	"yizhuoyan/go-study/pdfgo/raw/objects/indirectobject"
)

type BodyElement struct {
	RootDocumentCatalogRef *basicobject.IndirectObjectReference
	contentMap             map[int]*indirectobject.IndirectObject
	Locator                indirectobject.Locator
	pages                  []*contents.Page
}

func (this *BodyElement) ensureInit() {
	if this.contentMap != nil {
		return
	}
	this.contentMap = make(map[int]*indirectobject.IndirectObject, 16)

}

func (this *BodyElement) PutContent(objNum int, object *indirectobject.IndirectObject) {
	this.ensureInit()
	this.contentMap[objNum] = object
}
func (this *BodyElement) GetDocumentCatalog() (*contents.DocumentCatalog, error) {
	content, err := this.LoadIndirectObject(this.RootDocumentCatalogRef)
	if err != nil {
		return nil, err
	}
	if dic, is := content.Object.(*basicobject.Dictionary); is {
		rst := new(contents.DocumentCatalog)
		if err := rst.ParseFrom(dic, this); err != nil {
			return nil, err
		}
		return rst, nil
	}
	return nil, errors.Errorf("非DocumentCatalog")

}

func (this *BodyElement) LoadIndirectObject(ref *basicobject.IndirectObjectReference) (*indirectobject.IndirectObject, error) {
	this.ensureInit()
	var objNum = ref.ObjectNumber
	if rst, has := this.contentMap[objNum]; has {
		return rst, nil
	}
	indirectObject, err := this.Locator.LoadIndirectObject(ref)
	if err != nil {
		return nil, err
	}
	this.contentMap[objNum] = indirectObject
	return indirectObject, nil
}

func (this *BodyElement) GetPages() ([]*contents.Page, error) {
	if this.pages != nil {
		return this.pages, nil
	}
	catalog, err := this.GetDocumentCatalog()
	if err != nil {
		return nil, err
	}
	rootPagesIndirectObject, err := this.LoadIndirectObject(catalog.Pages)
	if err != nil {
		return nil, err
	}
	rootPages := new(contents.Pages)
	if err = rootPages.ParseRootFrom(rootPagesIndirectObject, this); err != nil {
		return nil, err
	}
	this.pages = make([]*contents.Page, 0, rootPages.Count)
	if err = this.parsePages(rootPages); err != nil {
		return nil, err
	}
	return this.pages, nil
}
func (this *BodyElement) parsePages(parent *contents.Pages) error {
	kidsArray := parent.Kids
	for i := 0; i < kidsArray.Length(); i++ {
		kid, err := this.LoadIndirectObject(kidsArray.MustGetIndirectObjectReference(i))
		if err != nil {
			return err
		}
		pageOrPages, hasErr := contents.NewPageOrPages(parent, kid)
		if hasErr != nil {
			return hasErr
		}
		if pageOrPages.IsPage() {
			this.pages = append(this.pages, pageOrPages.AsPage())
		} else {
			if err = this.parsePages(pageOrPages.AsPages()); err != nil {
				return err
			}
		}
	}
	return nil
}
